/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						       adventure_ai.h

	$Header: /game/adventure_ai.h $

	$NoKeywords: $

 ************************************************************************/


#ifndef _ADVENTURE_AI_HDR
#define _ADVENTURE_AI_HDR

#include "adventure_object_ptr.h"
#include "adventure_path_finder.h"
#include "counted_ptr.h"
#include "creature_array.h"
#include "creature_stack_ptr.h"
#include "danger_map.h"
#include "adv_dwelling_ptr.h"
#include "int_array.h"
#include "material_array.h"
#include "shared_ptr.h"
#include "sized_int_types.h"
#include "standard_c_file.h"
#include "town_ptr.h"
#include "town_type.h"

#include <fstream>
#include <list>
#include <map>
#include <vector>

class	t_adventure_map;
struct	t_adventure_path_data;
class	t_adventure_tile;
class	t_ai_army_data_cache;
class	t_ai_army_mover;
class	t_ai_unit;
class	t_army;
class	t_army_mover;
class	t_creature_array;
struct	t_default_hero;
class	t_player;
enum	t_spell;
class	t_town;
class	t_town_list;
enum	t_town_building;

const float k_ai_minimum_artifact_value_level = 250.0f;
const float	k_value_army_death = -500000.0f;
const float	k_lost_value_death_threshold = 0.95f;
const float k_value_victory = 1000000.0f;

// **********
// *** t_adv_ai_data_cache
// **********

typedef std::pair< t_creature_array const*, t_creature_array const* > t_creature_array_pair;

struct t_compare_creature_array_pair : public std::binary_function< std::string, std::string, bool >
{
	bool operator()( t_creature_array_pair const& left, t_creature_array_pair const& right ) const;
};

class t_adv_ai_data_cache 
{
public:
	struct t_distance_point
	{
		int						index;
		int						distance;
	};

	typedef std::vector< t_distance_point > t_distance_points;
	typedef t_int_array<k_town_type_count> t_alignment_array;
	
	t_adv_ai_data_cache();
	
	void	reset_player();
	void	reset_army();
	void	reset_danger();

	float	value_of_creature_swap( t_adventure_ai const& ai, t_creature_array const& target, 
									t_creature_array const& source );

	float	value_of_combat( t_creature_array const& attacker, t_creature_array const& defender );

	float	value_lost_in_combat( t_creature_array const& attacker, t_creature_array const& defender, bool attacker_location = false );

	t_alignment_array const&	get_alignment_modifiers( t_creature_array const* array );
	int							get_alignment_modifier( t_creature_array const* array, t_town_type alignment );

	float	get_ai_value( t_creature_stack const* stack );
	float	get_ai_value( t_creature_array const* army );
	float	get_ai_value( t_town const* town );

	t_distance_points const& get_distance_points( t_adventure_map* map, t_adv_map_point const& point, int move_points, t_creature_array* array );
	t_visited_adv_points const&	get_danger_points( t_adventure_map* map, t_creature_array* array );
	t_visited_adv_points const&	get_value_points( t_adventure_map* map, t_adventure_object const* object, t_player* player );

	void	reset_ai_value( t_creature_stack const* stack );
	void	reset_ai_value( t_creature_array const* army );
	void	reset_ai_value( t_town const* town );

protected:
	struct t_distance_map_info
	{
		t_distance_points		points;
		int						move_points;
		t_adv_map_point			position;
	};

	void	build_distance_points( t_adventure_map& map, t_adv_ai_data_cache::t_distance_map_info& info, 
								   t_adv_map_point const& location, int move_points, t_creature_array* array );

	typedef std::map< t_creature_array const*, t_alignment_array > t_alignment_modifier_map;
	typedef std::map< t_creature_array const*, float > t_army_value_map;
	typedef std::map< t_creature_stack const*, float > t_stack_value_map;
	typedef std::map< t_creature_array_pair, float, t_compare_creature_array_pair > t_creature_pair_map;
	typedef std::map< t_town const*, float > t_town_value_map;
	typedef std::map< t_adventure_object const*, t_visited_adv_points > t_value_points_map;
	typedef std::map< t_creature_array const*, t_visited_adv_points > t_danger_points_map;
	typedef std::map< t_creature_array const*, t_distance_map_info > t_distance_points_map;

	t_alignment_modifier_map	m_alignment_modifier_map;
	t_army_value_map			m_army_value_map;
	t_creature_pair_map			m_combat_value_map;
	t_creature_pair_map			m_combat_value_lost_map;
	t_danger_points_map			m_danger_points_map;
	t_distance_points_map		m_distance_points_map;
	t_stack_value_map			m_stack_value_map;
	t_creature_pair_map			m_swap_value_map;
	t_town_value_map			m_town_value_map;
	t_value_points_map			m_value_points_map;
};

inline bool t_compare_creature_array_pair::operator()( t_creature_array_pair const& left, 
													   t_creature_array_pair const& right ) const
{
	if (&left.first < &right.first)
		return true;

	if (&left.first > &right.first)
		return false;

	return &left.second < &right.second;
}

// **********
// *** generic
// **********

struct t_production_option
{
	t_material_array const* building_cost;
	t_town_building			building_type;
	t_material_array		cost;
	int						count;
	t_hero*					hero;
	int						per_value;
	int						total_value;
	t_creature_type			type;
};

typedef std::vector< t_production_option > t_production_options;

class t_find_production_type
{
public:
	t_find_production_type( t_creature_type type ) : m_type( type ) {}

	bool operator()( t_production_option& desc )
	{
		return desc.type == m_type;
	}
protected:
	t_creature_type m_type;
};

class t_production_value_order
{
public:
	bool operator()( t_production_option& left, t_production_option& right)
	{
		return left.total_value < right.total_value;
	} 
};

typedef std::list< t_creature_array* > t_creature_list;

int get_map_index( t_adventure_map& map, t_adv_map_point const& point );
void get_production_options( t_player* player, t_production_options& options, int count );
void reinforce_army( t_adventure_ai const& ai, t_creature_array& target, t_creature_array& source );
bool shuffle_armies( t_adventure_ai const& ai, t_creature_array& target, t_creature_array& source, bool& target_stronger, bool cleanup_empty = true );
void supplement_army( t_town& town, t_creature_array& target, t_creature_array& orphans );
bool survives_combat_series( t_creature_array* attacker, t_creature_list* defenders, float& value_loss );
float value_of_combat( t_creature_array const* attacker, t_creature_array const* defender );
float value_of_reinforcement( t_town const* town, t_creature_array const& target, t_creature_array const& source );
float value_lost_in_combat( t_creature_array const* attacker, t_creature_array const* defender, bool attacker_location = false);

std::auto_ptr< t_creature_array > ai_copy_of_creature_array( t_creature_array const& army );
std::auto_ptr< t_creature_array > ai_deep_copy_of_creature_array( t_creature_array const& army );

// **********
// *** t_adventure_ai
// **********

class t_adventure_ai 
{
public:
	typedef std::vector< t_spell > t_damage_spells;

	t_adventure_ai(t_adventure_map& map);
	~t_adventure_ai();

	bool						consider_alternate_destinations(bool clear_path_finder = false);
	void						do_neutral_player();
	void						do_turn( t_adventure_frame* frame );
	t_army*						get_army();
	t_material_array const&		get_boat_cost() const;
	t_adv_ai_data_cache&		get_cache() const;
	t_adventure_path const&		get_current_path() const;
	t_damage_spells const&		get_damage_spells() const;
	int							get_buildable_mage_guild_level() const;
	int							get_hero_count() const;
	float						get_land_coverage() const;
	int							get_mage_guild_level() const;
	float						get_rough_coverage() const;
	t_material_array const&		get_ship_cost() const;
	float						get_ship_construction_barrier() const;
	int							get_spell_immunity_levels() const;
	void						halt_movement();
	bool						ignore_pathing_danger() const;
	bool						impending_defeat() const;
	bool						moving_last_army() const;
	void						set_current_path( t_adventure_path const& path );
	bool						read( std::streambuf& stream, int version );
	void						reset_danger();
	void						start_game();
	float						scale_for_aggression( t_player const* source, t_player const* destination ) const;
	float						scale_for_combat_aggression( t_player const* source, t_player const* destination ) const;
	void						visit_town( t_town& town, t_creature_array& visitor );
	bool						write( std::streambuf& stream ) const;

protected:
	typedef std::vector<t_uint16>						t_distance_map;
	typedef std::list< t_adventure_tile* >				t_tile_list;
	typedef std::vector<float>							t_value_map;

	enum t_wander_target
	{
		k_wander_target_none,
		k_wander_target_tile,
		k_wander_target_object
	};

	void						apply_trim( int safe_point, int stop_point, t_adv_map_point const & destination );
	void						apply_value_of_building_prerequisites( t_town* town, float* product_values );
	void						attempt_move( t_adv_map_point const& point, bool consider_alt_dests = true );
	void						build_damage_spell_list();
	void						build_danger_map();
	void						build_distance_map();
	void						calculate_land_coverage();
	void						calculate_mage_guild_level();
	void						calculate_spell_immunity_levels();
	void						calculate_threat();
	int							calculate_value_of_alt_location( t_adv_map_point const& point, float& location_value );
	float						calculate_value_of_location(t_adv_map_point const& point);
	bool						can_create_army() const;
	bool						choose_alternate_destination( t_adv_map_point& best_destination, float& actual_value );
	bool						choose_destination(int max_distance, t_adv_map_point& best_destination, float& value);
	void						clear_danger_map();
	void						clear_distance_map();
	void						clear_shipyards();
	void						clear_value_map();
	void						consider_army_creation();
	void						consider_building_options();
	void						consider_caravans();
	void						consider_dwelling_caravan( t_adv_dwelling_ptr dwelling_ptr, t_town_list& caravan_towns );
	void						consider_town_caravans( t_town_list& caravan_town );
	void						convert_town_values_to_scaling_factors( int count, std::vector<float>& town_values );
	void						count_assets();
	void						end_turn();
	void						find_potential_destinations( t_map_point_array& destinations, int max_distance, bool alt_dest = false );
	bool						get_next_army();
	float						get_product_value( t_town* town, t_town_building building );
	void						get_point_indices( t_adv_map_point const& point, int& danger_index, int& point_index );
	void						log_path( int safe_point );
	void						mark_army_on_danger_map( t_creature_array* enemy_army, bool build_bounty );
	void						mark_army_on_distance_map( t_army* friendly_army );
	void						mark_object_on_value_map( t_adventure_object const& object, float value );
	void						mark_shipyards();
	void						move_current_army();
	void						process_wandering_army( t_army& army );
	void						process_garrisoned_army();
	void						process_guarding_army( t_army& army );
	void						reinforce_threatened_towns();
	void						reinforce_threatened_town( t_town_ptr town_ptr );
	t_wander_target				select_wandering_destination( t_army& army, int direction, int range, bool only_targets, bool randomize_direction, int& dominate_direction );
	void						set_current_army( t_creature_array* army, bool build_danger = true );
	bool						skip_danger_validation( t_creature_array* army ) const;
	void						start_turn();
	void						trim_path( t_adv_map_point const& destination );
	void						update_invalidated_threat();

	t_adv_map_point				m_alt_dest;
	int							m_army_count;
	int							m_base_map_levels;
	int							m_base_map_size;
	t_material_array			m_boat_cost;
	int							m_buildable_mage_guild_level;
	bool						m_built_bounty;
	t_creature_array*			m_current_army;
	t_adventure_object_ptr		m_current_army_object;
	t_ai_army_data_cache*		m_current_army_cache;
	float						m_current_army_danger;
	bool						m_current_army_in_danger;
	int							m_current_army_index;
	t_adv_map_point				m_current_army_position;
	float						m_current_army_value;
	int							m_current_team;
	t_adventure_path			m_current_path;
	t_player*					m_current_player;
	t_damage_spells				m_damage_spells;
	t_danger_map_ptr			m_danger_map_ptr;
	mutable t_adv_ai_data_cache	m_data_cache;
	t_distance_map				m_distance_map;
	t_map_point_array			m_essential_dests;
	bool						m_explored_once;
	bool						m_has_alt_dest;
	int							m_hero_count;
	float						m_land_coverage;
	int							m_last_caravan_usage;
	t_creature_array*			m_last_army;
	std::ofstream				m_log_file;
	std::ofstream				m_log_file_profiling;
	bool						m_logging;
	t_adventure_map&			m_map;
	int							m_map_size;
	t_tile_list					m_marked_ships;
	t_counted_ptr<t_ai_army_mover>	m_mover;
	unsigned int				m_move_number;
	bool						m_moving_last_army;
	t_creature_array*			m_our_threat;
	t_adventure_path_finder		m_path_finder;
	t_progress_handler*			m_progress_handler;
	float						m_rough_coverage;
	bool						m_single_threat;
	int							m_spell_immunity_levels;
	int							m_ship_construction_barrier;
	bool						m_threatened_town;
	t_value_map					m_value_map;
};

inline t_adv_ai_data_cache& t_adventure_ai::get_cache() const
{
	return m_data_cache;
}

inline t_material_array const& t_adventure_ai::get_boat_cost() const
{
	return m_boat_cost;
}

inline bool t_adventure_ai::moving_last_army() const
{
	return m_moving_last_army;
}

// **********
// *** t_ai_army
// **********

class t_ai_army : public t_creature_array
{
public:
	t_ai_army( t_adventure_map* map, t_player* owner );

	virtual	t_adventure_ai const*	get_ai() const;
	virtual t_adventure_map*		get_map() const;
	virtual int						get_owner_number() const;


private:
	t_adventure_map* m_map;
	t_player* m_owner;
};


// **********
// *** t_ai_army_builder
// **********

class t_ai_army_builder
{
public:
	typedef std::bitset< t_creature_array::k_size > t_locked_creatures;

	t_ai_army_builder( t_adventure_ai const& ai, t_creature_array const& target, t_creature_array const& source, bool fixed_target = false );

	void			build_armies( t_creature_array& stronger, t_creature_array& weaker );
	bool			is_target_stronger() const;
	float			shuffle_armies();
	void			swap_artifacts( t_creature_array& stronger, t_creature_array& weaker );

protected:
	typedef std::vector< t_creature_stack_ptr > t_builder_creature_array;

	void			add_army( t_creature_array const& army );
	void			add_creature( t_creature_stack const& stack );
	void			build_initial_armies( t_creature_array const& target, t_creature_array const& source );
	bool			find_best_swap(int& source, int& destination);
	void			init( t_creature_array const& target, t_creature_array const& source );
	bool			merge_with_existing_data( t_creature_stack const& stack );
	void			transfer_armies( t_creature_array const& target, t_creature_array const& source );

	t_adventure_ai const&		m_ai;
	bool						m_both_locked;
	t_builder_creature_array	m_creatures;
	bool						m_fixed_target;
	t_ai_army					m_greater_army;
	t_locked_creatures			m_greater_army_locks;
	float						m_initial_value;
	t_ai_army					m_lesser_army;
	t_locked_creatures			m_lesser_army_locks;
	bool						m_target_stronger;

};

// **********
// *** t_creature_purchaser
// **********

class t_creature_purchaser 
{
public:
	t_creature_purchaser( t_town const& town, t_creature_array const& army, 
						  t_creature_array const* orphan_army, bool purchased_building = false, 
						  bool include_heroes = true );
	t_creature_purchaser( t_adv_dwelling const& dwelling, t_creature_array const& army );

	// Constructor used by the caravan consideration code
	t_creature_purchaser( t_town const& town );

	virtual ~t_creature_purchaser();

	void						apply_charges( t_town& town );
	t_material_array const&		get_cost() const;
	void						go_shopping( t_town& town, 
												t_creature_array& army, 
												t_creature_array* ophan_army, 
												bool freebies = false );
	void						go_shopping( t_adv_dwelling& dwelling, t_creature_array& army );
	float						query_shopping_value();


protected:
	typedef std::map< t_creature_type, int > t_creatures_spent;
	typedef std::vector< t_creature_stack_ptr > t_hero_vector;
	
	void			calculate_dwelling_purchase_options( t_adv_dwelling const& dwelling );
	virtual	void	calculate_town_purchase_options( t_town const& town );
	void			calculate_tavern_options( t_town const& town );
	float			do_best_purchase();
	virtual void	execute_best_purchase( t_production_options::iterator best_iter, 
										   int best_index, int best_amount, int day );
	void			save_cost();

	t_owned_ptr<t_creature_array>		m_army;
	t_material_array					m_cost;
	t_creatures_spent					m_creatures_spent;
	bool								m_include_heroes;
	t_material_array					m_funds;
	std::vector<t_default_hero>			m_included_default_heroes;
	t_hero_vector						m_included_heroes;
	t_adventure_map const*				m_map;
	t_production_options				m_options;
	t_player*							m_owner;
	t_creature_array const*				m_original_army;
	t_creature_array const*				m_original_orphan;
	t_owned_ptr<t_creature_array>		m_orphan_army;
	bool								m_purchased_building;
	t_town_building						m_purchased_building_type;
	t_hero*								m_purchased_hero;
	bool								m_using_discretionary_funds;
};

void request_player( t_adventure_map& map, t_player const* player );

#endif